package appmgr

import (
	"fmt"
	"net"
	"network"
	cm "share/msg"
	"sync"
	"util/log"
)

type AppSvr struct {
	run       bool
	ip        string
	port      int
	sock      *network.SvrSock
	quit      chan bool
	appclient map[network.ConnId]*cm.AppInfo
	lock      sync.RWMutex
}

func (app *AppSvr) Start() bool {
	if app.run {
		return false
	}

	s := network.NewSvrSock(app, 2048, 1024)
	addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", app.ip, app.port))
	if err != nil {
		log.LogError(err)
		return false
	}

	if err = s.Listen(addr, 1); err == nil {
		app.run = true
		app.sock = s
		return true
	}

	log.LogError(err)

	return false
}

func (app *AppSvr) Stop() {
	if app.run {
		app.sock.Shutdown()
		app.run = false
	}
	app.quit <- true
}

func (app *AppSvr) Done() {
	select {
	case <-app.quit:
		break
	}
}

func (app *AppSvr) OnAccept(id network.ConnId, addr net.Addr) {
	app.lock.Lock()
	defer app.lock.Unlock()

	if _, dup := app.appclient[id]; dup {
		panic("dump key")
	}

	app.appclient[id] = &cm.AppInfo{}

	log.TraceInfo("appbook", "new client: ", addr.String())

}

func (app *AppSvr) OnClose(id network.ConnId, addr net.Addr) {
	app.lock.Lock()
	defer app.lock.Unlock()

	if _, ok := app.appclient[id]; !ok {
		panic("not exist")
	}

	delete(app.appclient, id)

	log.TraceInfo("appbook", "close client: ", addr.String())
}

func (app *AppSvr) OnReceive(id network.ConnId, data []byte) {

	msg := cm.CenterMsg{}
	err := cm.DecodeMsg(data, &msg)
	if err != nil {
		log.LogError("handlemsg error", err)
		return
	}

	switch msg.MsgId {
	case cm.APP_MSG_REG:
		res := app.regApp(id, data)
		app.regAppBak(id, res)
		app.syncAppList()
	case cm.APP_MSG_PING:
		app.pong(id)
	case cm.APP_MSG_STATUS:
		if res := app.status(id, data); res {
			app.syncAppList()
		}
	}
}

func (app *AppSvr) regApp(id network.ConnId, data []byte) uint64 {
	app.lock.Lock()
	defer app.lock.Unlock()

	msg := cm.RegApp{}
	err := cm.DecodeMsg(data, &msg)
	if err != nil {
		log.LogError("RegApp{} msg parse error")
		return 0
	}

	for _, a := range app.appclient {
		if a.AppId == msg.App.AppId {
			log.LogError("app id dump")
			return 0
		}
	}

	appinfo := app.appclient[id]
	appinfo.AppId = msg.App.AppId
	appinfo.AppType = msg.App.AppType
	appinfo.AppIp = msg.App.AppIp
	appinfo.AppPort = msg.App.AppPort

	log.TraceInfo("appbook", "app reg: ", appinfo.AppId)
	return appinfo.AppId

}

func (app *AppSvr) regAppBak(id network.ConnId, appid uint64) {
	msg := cm.RegAppBak{}
	msg.MsgId = cm.APP_MSG_REG_BAK
	msg.AppId = appid

	buf, _ := cm.EncodeMsg(msg)
	app.sock.Send(id, buf)
}

func (app *AppSvr) status(id network.ConnId, data []byte) bool {
	app.lock.Lock()
	defer app.lock.Unlock()

	status := cm.Status{}

	err := cm.DecodeMsg(data, &status)
	if err != nil {
		return false
	}

	app.appclient[id].LoadPoint = status.LoadPoint

	return true

}

func (app *AppSvr) pong(id network.ConnId) {
	ping := cm.Pong{}
	ping.MsgId = cm.APP_MSG_PONG
	if buf, err := cm.EncodeMsg(ping); err == nil {
		app.sock.Send(id, buf)
	} else {
		panic("encode msg failed")
	}
}

func (app *AppSvr) appList() cm.AppListBak {
	applist := cm.AppListBak{}
	applist.MsgId = cm.APP_MSG_LIST_BAK
	applist.Apps = make([]cm.AppInfo, 0, len(app.appclient))
	for _, ai := range app.appclient {
		if ai.AppId == 0 {
			continue
		}
		applist.Apps = append(applist.Apps, *ai)
	}
	return applist
}

func (app *AppSvr) syncAppList() {

	applist := app.appList()

	if buf, err := cm.EncodeMsg(applist); err == nil {
		app.sock.Broadcast(buf)
	}
}

func NewAppSvr(ip string, port int) *AppSvr {
	as := &AppSvr{}
	as.ip = ip
	as.port = port
	as.quit = make(chan bool, 1)
	as.appclient = make(map[network.ConnId]*cm.AppInfo)
	return as
}
